package org.little.beans.factory.support;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.little.beans.BeanDefinition;
import org.little.beans.ConstructorArgument;
import org.little.beans.SimpleTypeConverter;
import org.little.beans.factory.BeanCreationException;

import java.lang.reflect.Constructor;
import java.util.List;

/**
 * 多个构造函数做匹配
 * @author Tinyspot
 * @date 2022-05-22 14:02
 */
public class ConstructorResolver {

    protected final Log logger = LogFactory.getLog(getClass());

    private final AbstractBeanFactory beanFactory;

    public ConstructorResolver(AbstractBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public Object autowireConstructor(final BeanDefinition beanDefinition) {
        Constructor<?> constructorToUse = null;
        Object[] argsToUse = null;

        Class<?> beanClass = null;
        try {
            beanClass = this.beanFactory.getBeanClassLoader().loadClass(beanDefinition.getBeanClassName());
        } catch (ClassNotFoundException e) {
            throw new BeanCreationException(beanDefinition.getId(), "Instantiation of bean failed, can't resolve class", e);
        }

        Constructor<?>[] candidates = beanClass.getConstructors();
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory);
        ConstructorArgument consArgs = beanDefinition.getConstructorArgument();
        SimpleTypeConverter typeConverter = new SimpleTypeConverter();
        for (int i = 0; i < candidates.length; i++) {
            Class<?>[] parameterTypes = candidates[i].getParameterTypes();

            if (parameterTypes.length != consArgs.getArgumentCount()) {
                // 参数数量不等，不处理
                continue;
            }
            argsToUse = new Object[parameterTypes.length];
            boolean result = this.valuesMatchType(parameterTypes, consArgs.getArgumentValues(), argsToUse, valueResolver, typeConverter);
            if (result) {
                constructorToUse = candidates[i];
                break;
            }
        }

        // 找不到合适的构造器
        if (constructorToUse == null) {
            throw new BeanCreationException(beanDefinition.getId(), "can't find a appropriate constructor");
        }

        try {
            return constructorToUse.newInstance(argsToUse);
        } catch (Exception e) {
            throw new BeanCreationException(beanDefinition.getId(), "can't find a create instance using " + constructorToUse);
        }
    }

    public boolean valuesMatchType(Class<?> [] parameterTypes, List<ConstructorArgument.ValueHolder> valueHolders,
                                   Object[] argsToUse, BeanDefinitionValueResolver valueResolver, SimpleTypeConverter typeConverter) {
        for (int i = 0; i < parameterTypes.length; i++) {
            ConstructorArgument.ValueHolder valueHolder = valueHolders.get(i);
            // TypeStringValue OR RuntimeBeanReference
            Object originalValue = valueHolder.getValue();
            try {
                Object resolveValue = valueResolver.resolveValueIfNecessary(originalValue);
                Object convertedValue = typeConverter.convertIfNecessary(resolveValue, parameterTypes[i]);
                argsToUse[i] = convertedValue;
            } catch (Exception e) {
                logger.error(e);
                return false;
            }
        }
        return true;
    }
}
